package view;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JLabel;

import java.awt.Font;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;
import java.awt.Color;
import java.awt.event.ActionListener;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Vector;

import javax.swing.JSeparator;
import javax.swing.border.TitledBorder;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import javax.swing.JComboBox;
import javax.swing.JButton;

import model.FrequencyPair;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import javax.swing.SwingConstants;
import java.awt.event.ActionEvent;

public class MainFrame extends JFrame{
	
	public static final String RANDOM = "Random";
	public static final String UNIFORM = "Uniform";
	public static final String SKEWED_LEFT = "Skewed Left";
	public static final String SKEWED_RIGHT = "Skewed Right";
	public static final String BIMODAL = "Bimodal";
	public static final String NORMAL = "Normal";
	
	private JComboBox<String> cbDistributionType;
	private JButton btnGenerate;
	private JButton btnGenerateSample;
	private JButton btnPopDec;
	private JButton btnPopInc;
	private JButton btnSampInc;
	private JButton btnSampDec;
	private JButton btnChange;
	
	private JTextField tfLowerBound;
	private JTextField tfUpperBound;
	private JTextField tfPopulationSize;
	private JTextField tfSampleSize;
	private JTextField tfInterval;
	
	private JTextField tfPopMean;
	private JTextField tfPopVar;
	private JTextField tfSampMean;
	private JTextField tfSampVar;
	
	private JTable popDistTable;
	private TableModel freqDist;
	private ChartPanel popDistGraph;
	private ChartPanel sampDistGraph;
	
	private Integer interval;
	
	public MainFrame(){
		setTitle("ADVSTAT Project 1");
		initGUI();
		initializeFrameSettings();
	}
	
	private void initGUI(){
		getContentPane().setLayout(null);
		
		JPanel inputPanel = new JPanel();
		inputPanel.setBorder(new TitledBorder(new LineBorder(new Color(0, 0, 0), 2), "Input", TitledBorder.LEFT, TitledBorder.TOP, null, null));
		inputPanel.setBounds(10, 11, 924, 125);
		getContentPane().add(inputPanel);
		inputPanel.setLayout(null);
		
		JLabel lblLower = new JLabel("Lower Bound:");
		lblLower.setBounds(24, 65, 94, 29);
		inputPanel.add(lblLower);
		lblLower.setFont(new Font("Franklin Gothic Heavy", Font.PLAIN, 14));
		
		tfLowerBound = new JTextField();
		tfLowerBound.setHorizontalAlignment(SwingConstants.CENTER);
		tfLowerBound.setBounds(24, 90, 86, 20);
		inputPanel.add(tfLowerBound);
		tfLowerBound.setColumns(10);
		
		JLabel lblUpperBound = new JLabel("Upper Bound:");
		lblUpperBound.setBounds(147, 65, 94, 29);
		inputPanel.add(lblUpperBound);
		lblUpperBound.setFont(new Font("Franklin Gothic Heavy", Font.PLAIN, 14));
		
		tfUpperBound = new JTextField();
		tfUpperBound.setHorizontalAlignment(SwingConstants.CENTER);
		tfUpperBound.setBounds(147, 90, 86, 20);
		inputPanel.add(tfUpperBound);
		tfUpperBound.setColumns(10);
		
		JLabel lblTo = new JLabel("to");
		lblTo.setBounds(120, 90, 29, 14);
		inputPanel.add(lblTo);
		lblTo.setFont(new Font("Tahoma", Font.PLAIN, 14));
		
		JLabel lblPopulationSize = new JLabel("Population Size:");
		lblPopulationSize.setHorizontalAlignment(SwingConstants.CENTER);
		lblPopulationSize.setFont(new Font("Franklin Gothic Heavy", Font.PLAIN, 14));
		lblPopulationSize.setBounds(344, 13, 230, 29);
		inputPanel.add(lblPopulationSize);
		
		JLabel lblSampleSize = new JLabel("Sample Size:");
		lblSampleSize.setHorizontalAlignment(SwingConstants.CENTER);
		lblSampleSize.setFont(new Font("Franklin Gothic Heavy", Font.PLAIN, 14));
		lblSampleSize.setBounds(637, 13, 230, 29);
		inputPanel.add(lblSampleSize);
		
		tfPopulationSize = new JTextField();
		tfPopulationSize.setHorizontalAlignment(SwingConstants.CENTER);
		tfPopulationSize.setColumns(10);
		tfPopulationSize.setBounds(419, 42, 86, 20);
		inputPanel.add(tfPopulationSize);
		
		tfSampleSize = new JTextField();
		tfSampleSize.setHorizontalAlignment(SwingConstants.CENTER);
		tfSampleSize.setColumns(10);
		tfSampleSize.setBounds(709, 42, 86, 20);
		inputPanel.add(tfSampleSize);
		
		btnGenerate = new JButton("Generate Population Distribution");
		btnGenerate.setBounds(344, 75, 230, 23);
		inputPanel.add(btnGenerate);
		
		btnGenerateSample = new JButton("Generate Sample Mean Distribution");
		btnGenerateSample.setBounds(637, 75, 230, 23);
		inputPanel.add(btnGenerateSample);
		
		btnPopInc = new JButton("+ 1");
		btnPopInc.setBounds(512, 41, 62, 23);
		inputPanel.add(btnPopInc);
		
		btnPopDec = new JButton("- 1");
		btnPopDec.setBounds(344, 41, 62, 23);
		inputPanel.add(btnPopDec);
		
		JLabel lblTypeOfDistribution = new JLabel("Distribution Type:");
		lblTypeOfDistribution.setHorizontalAlignment(SwingConstants.CENTER);
		lblTypeOfDistribution.setBounds(49, 13, 156, 29);
		inputPanel.add(lblTypeOfDistribution);
		lblTypeOfDistribution.setFont(new Font("Franklin Gothic Heavy", Font.PLAIN, 14));
		
		cbDistributionType = new JComboBox();
		cbDistributionType.setBounds(49, 42, 156, 20);
		inputPanel.add(cbDistributionType);
		cbDistributionType.addItem(RANDOM);
		cbDistributionType.addItem(UNIFORM);
		cbDistributionType.addItem(SKEWED_LEFT);
		cbDistributionType.addItem(SKEWED_RIGHT);
		cbDistributionType.addItem(BIMODAL);
		cbDistributionType.addItem(NORMAL);
		
		btnSampInc = new JButton("+ 1");
		btnSampInc.setBounds(805, 39, 62, 23);
		inputPanel.add(btnSampInc);
		
		btnSampDec = new JButton("- 1");
		btnSampDec.setBounds(637, 39, 62, 23);
		inputPanel.add(btnSampDec);
		
		JLabel lblInterval = new JLabel("Interval");
		lblInterval.setHorizontalAlignment(SwingConstants.CENTER);
		lblInterval.setFont(new Font("Franklin Gothic Heavy", Font.PLAIN, 14));
		lblInterval.setBounds(256, 13, 78, 29);
		inputPanel.add(lblInterval);
		
		tfInterval = new JTextField();
		tfInterval.setHorizontalAlignment(SwingConstants.CENTER);
		tfInterval.setText("1");
		tfInterval.setBounds(256, 42, 78, 20);
		inputPanel.add(tfInterval);
		tfInterval.setColumns(10);
		
		btnChange = new JButton("change");
		btnChange.setBounds(256, 75, 78, 23);
		inputPanel.add(btnChange);
		
		JSeparator divider2 = new JSeparator();
		divider2.setOrientation(SwingConstants.VERTICAL);
		divider2.setBounds(580, 13, 20, 101);
		inputPanel.add(divider2);
		
		JSeparator divider1 = new JSeparator();
		divider1.setOrientation(SwingConstants.VERTICAL);
		divider1.setBounds(243, 13, 20, 101);
		inputPanel.add(divider1);
		
		JPanel outputPanel = new JPanel();
		outputPanel.setBorder(new TitledBorder(new LineBorder(new Color(0, 0, 0), 2), "Output", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		outputPanel.setBounds(10, 147, 924, 367);
		getContentPane().add(outputPanel);
		outputPanel.setLayout(null);
		
		JScrollPane scrollPane = new JScrollPane();
		scrollPane.setBounds(10, 15, 230, 340);
		outputPanel.add(scrollPane);
		
		popDistTable = new JTable();
		scrollPane.setViewportView(popDistTable);
		
		popDistGraph = new ChartPanel(null);
		popDistGraph.setBounds(247, 15, 330, 290);
		outputPanel.add(popDistGraph);
		
		sampDistGraph = new ChartPanel(null);
		sampDistGraph.setMouseWheelEnabled(true);
		sampDistGraph.setBounds(584, 15, 330, 290);
		outputPanel.add(sampDistGraph);
		
		tfPopMean = new JTextField();
		tfPopMean.setBounds(278, 336, 130, 20);
		outputPanel.add(tfPopMean);
		tfPopMean.setColumns(10);
		
		tfPopVar = new JTextField();
		tfPopVar.setBounds(418, 336, 130, 20);
		outputPanel.add(tfPopVar);
		tfPopVar.setColumns(10);
		
		tfSampMean = new JTextField();
		tfSampMean.setColumns(10);
		tfSampMean.setBounds(612, 336, 130, 20);
		outputPanel.add(tfSampMean);
		
		tfSampVar = new JTextField();
		tfSampVar.setColumns(10);
		tfSampVar.setBounds(752, 336, 130, 20);
		outputPanel.add(tfSampVar);
		
		JLabel lblPopMean = new JLabel("Population Mean");
		lblPopMean.setHorizontalAlignment(SwingConstants.CENTER);
		lblPopMean.setFont(new Font("Franklin Gothic Heavy", Font.PLAIN, 13));
		lblPopMean.setBounds(278, 306, 130, 29);
		outputPanel.add(lblPopMean);
		
		JLabel lblPopVar = new JLabel("Population Variance");
		lblPopVar.setHorizontalAlignment(SwingConstants.CENTER);
		lblPopVar.setFont(new Font("Franklin Gothic Heavy", Font.PLAIN, 13));
		lblPopVar.setBounds(418, 306, 130, 29);
		outputPanel.add(lblPopVar);
		
		JLabel lblSampMean = new JLabel("Sample Mean");
		lblSampMean.setHorizontalAlignment(SwingConstants.CENTER);
		lblSampMean.setFont(new Font("Franklin Gothic Heavy", Font.PLAIN, 13));
		lblSampMean.setBounds(612, 306, 130, 29);
		outputPanel.add(lblSampMean);
		
		JLabel lblSampVariance = new JLabel("Sample Variance");
		lblSampVariance.setHorizontalAlignment(SwingConstants.CENTER);
		lblSampVariance.setFont(new Font("Franklin Gothic Heavy", Font.PLAIN, 13));
		lblSampVariance.setBounds(752, 306, 130, 29);
		outputPanel.add(lblSampVariance);
		
		interval = 1;
	}
	
	private void initializeFrameSettings(){
		setSize(950, 550);
		setResizable(false);
		setLocationRelativeTo(null);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);	
	}
	
	// Display Messages
	public static void displayError(String error){
		JOptionPane.showMessageDialog(null, error, "Error!", JOptionPane.ERROR_MESSAGE);
	}
	
	public static void displayWarning(String warning){
		JOptionPane.showMessageDialog(null, warning, "Warning!", JOptionPane.WARNING_MESSAGE);
	}
	
	// Input Checkers
	public boolean isInputValidForGeneratePopulation(){
		int LB = 0;
		int UB = 0;
		int N = 0;
		
		try{
			LB = get_LB();
			UB = get_UB();
			N =get_N();
		}
		catch(Exception e){ return false;}
		
		if(cbDistributionType.getSelectedItem() == null)
			return false;
		
		if(LB < 1 || UB < 1 || N < 1){
			displayError("Input values must be positive integers.");
			return false;
		}
		
		if(LB > UB){
			displayError("Lower Bound must be less than or equal to the Upper Bound.");
			return false;
		}
		
		return true;
	}
	
	public boolean isInputValidForGenerateSample(){
		if(!isInputValidForGeneratePopulation())
			return false;
		
		int N = 0;
		int n = 0;
		
		try{
			N = get_N();
			n = get_n();
		}
		catch(Exception e){ return false;}
		
		if(n > 6)
			displayWarning("The Sample Size is big and the computation might take a while.");
		
		return true;
	}

	// Setters
	public void setPopulationGraphContent(ArrayList<FrequencyPair> frequencyList){
		XYSeries series = new XYSeries("Frequency Distribution");
		 
		for(FrequencyPair pair : frequencyList){
			series.add(pair.getNumAsDouble(), pair.getFrequency());
		}
		 
		// Add the series to your data set
		XYSeriesCollection dataset = new XYSeriesCollection();
		dataset.addSeries(series);
		 	 
		//Generate graph
		JFreeChart chart = ChartFactory.createHistogram(
					"Frequency Distribution",	"x",	"frequency", 
		            dataset,	PlotOrientation.VERTICAL,
		            true,	true,	false
			        );
		
		this.popDistGraph.setChart(chart);
	}
	
	public void setSampleGraphContent(ArrayList<FrequencyPair> sampleMeanFreq){
		XYSeries series = new XYSeries("Sample Mean Distribution");
		 
		for(FrequencyPair pair : sampleMeanFreq){
			series.add(pair.getNumAsDouble(), pair.getFrequency());
		}
		 
		// Add the series to your data set
		XYSeriesCollection dataset = new XYSeriesCollection();
		dataset.addSeries(series);
		 	 
		//Generate graph
		JFreeChart chart = ChartFactory.createHistogram(
					"Sample Mean Distribution",	"mean", "frequency", 
		            dataset,	PlotOrientation.VERTICAL,
		            true,	true,	false
			        );
		
		this.sampDistGraph.setChart(chart);
	}
	
	public void setPopDistTableContent(ArrayList<FrequencyPair> frequencyCount){
		// Column Headers
		Vector<String> headers = new Vector<String>();
		headers.add("x");
		headers.add("frequency");
		
		freqDist = new DefaultTableModel(headers, frequencyCount.size());
			
		int i = 0;
		for(FrequencyPair pair : frequencyCount){
			freqDist.setValueAt(pair.getNum(), i, 0);
			freqDist.setValueAt(pair.getFrequency(), i, 1);
			i++;
		}
		
		popDistTable.setModel(freqDist);
		popDistTable.revalidate();
	}
	
	public void setTfPopMean(BigDecimal mean){
		String str = mean.toString();
		
		if(str.length() < 20)
			tfPopMean.setText(str);
		else
			tfPopMean.setText(str.substring(0, 19));
	}
	public void setTfPopVar(BigDecimal var){
		String str = var.toString();
		
		if(str.length() < 20)
			tfPopVar.setText(str);
		else
			tfPopVar.setText(str.substring(0, 19));
	}
	public void setTfSampMean(BigDecimal mean){
		String str = mean.toString();
		

		if(str.length() < 20)
			tfSampMean.setText(str);
		else
			tfSampMean.setText(str.substring(0, 19));
	}
	public void setTfSampVar(BigDecimal var){
		String str = var.toString();

		if(str.length() < 20)
			tfSampVar.setText(str);
		else
			tfSampVar.setText(str.substring(0, 19));
	}
	
	public void setBtnGeneratePopulationListener(ActionListener listener){
		setButtonListener(btnGenerate, listener);
	}
	public void setBtnGenerateSampleMeanDistListener(ActionListener listener){
		setButtonListener(btnGenerateSample, listener);
	}
	public void setBtnPopDec(ActionListener listener){
		setButtonListener(btnPopDec, listener);
	}
	public void setBtnPopInc(ActionListener listener){
		setButtonListener(btnPopInc, listener);
	}
	public void setBtnSampDec(ActionListener listener){
		setButtonListener(btnSampDec, listener);
	}
	public void setBtnSampInc(ActionListener listener){
		setButtonListener(btnSampInc, listener);
	}
	public void setBtnChange(ActionListener listener){
		setButtonListener(btnChange, listener);
	}
	
	private void setButtonListener(JButton button, ActionListener listener){
		if(button != null && listener != null){
			if(button.getActionListeners().length > 0)
				button.removeActionListener(button.getActionListeners()[0]);
			
			button.addActionListener(listener);
		}
	}
	
	public void offset_N(int offset) throws Exception{
		tfPopulationSize.setText(Integer.toString(get_N() + offset));
	}
	public void offset_n(int offset) throws Exception{
		tfSampleSize.setText(Integer.toString(get_n() + offset));
	}
	public void set_Interval(int interval){
		this.interval = interval;
		
		btnPopInc.setText("+ " + interval);
		btnPopDec.setText("- " + interval);
	}
	
	// Getters
	public Integer get_LB() throws Exception{
		return Integer.parseInt(tfLowerBound.getText().trim());
	}
	public Integer get_UB() throws Exception{
		return Integer.parseInt(tfUpperBound.getText().trim());
	}
	public Integer get_N() throws Exception{
		return Integer.parseInt(tfPopulationSize.getText().trim());
	}
	public Integer get_n() throws Exception{
		return Integer.parseInt(tfSampleSize.getText().trim());
	}
	public String getDistributionType(){
		return cbDistributionType.getSelectedItem().toString();
	}
	public Integer get_Interval() throws Exception{
		return Integer.parseInt(tfInterval.getText().trim());
	}
}